package org.example.fboot.config;

import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.web.server.util.matcher.OrServerWebExchangeMatcher;
import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.server.WebFilter;
import org.springframework.web.util.pattern.PathPattern;
import org.springframework.web.util.pattern.PathPatternParser;
import reactor.core.publisher.Mono;

import java.util.*;

@Slf4j
@Configuration
public class WebConfig {

    private static final String[] ignoreAuthUrls = {"/favicon.ico", "*/*.js"};

    /**
     * 跨域设置
     *
     * @return
     */
    @Bean
    public CorsWebFilter coreWebFilter() {
        CorsConfiguration corsConfig = new CorsConfiguration();
        corsConfig.addAllowedOrigin("*");
        corsConfig.addAllowedMethod("*");
        corsConfig.addAllowedHeader("*");
        corsConfig.addAllowedOriginPattern("*");
        corsConfig.setMaxAge(8000L);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", corsConfig);
        return new CorsWebFilter(source);
    }

    /**
     * 设置websocket校验
     * @return
     */
    @Bean
    public WebFilter webSocketAuthorization() {
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            if (request.getHeaders().containsKey("Sec-Websocket-Protocol")){
                ServerHttpResponse response = exchange.getResponse();
                response.getHeaders().add("Sec-Websocket-Protocol",request.getHeaders().getFirst("Sec-Websocket-Protocol"));
            }
            return chain.filter(exchange);
        };
    }

    /**
     * 跟踪请求
     *
     * @return {@link WebFilter}
     */
    @Bean
    public WebFilter setReqIdFilter() {
        log.info("添加reqID");
        final String reqIdKey = "reqID";
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            ServerHttpResponse response = exchange.getResponse();
            String reqId = UUID.randomUUID().toString().replaceAll("-", "");
            if (request.getHeaders().containsKey(reqIdKey)) {
                reqId = Objects.requireNonNull(request.getHeaders().get(reqIdKey)).getFirst();
            }
            MDC.put(reqIdKey, reqId);
            response.getHeaders().add(reqIdKey, reqId);
            return chain.filter(exchange);
        };
    }

    /**
     * 忽略静态文件
     *
     * @return
     */
    @Bean
    public WebFilter ignoreUrl(OrServerWebExchangeMatcher matcher) {
        log.info("添加忽略url:{}", ignoreAuthUrls);
        return (exchange, chain) ->
                matcher.matches(exchange)
                .flatMap(matchResult ->
                        matchResult.isMatch() ? Mono.empty() : chain.filter(exchange)
                );
    }

    /**
     * 添加忽略
     *
     * @return
     */
    @Bean
    public OrServerWebExchangeMatcher ignoreUrlWebExchangeMatcher() {
        return new OrServerWebExchangeMatcher(parser(ignoreAuthUrls));
    }

    /**
     * 添加忽略
     *
     * @return
     */


    private List<ServerWebExchangeMatcher> parser(String[] ignoreAuthUrls) {
        PathPatternParser parser = new PathPatternParser();
        List<ServerWebExchangeMatcher> pathPatterns = new ArrayList<>(ignoreAuthUrls.length);
        for (String url : ignoreAuthUrls) {
            String initUrl = parser.initFullPathPattern(url);
            PathPattern parse = parser.parse(initUrl);
            pathPatterns.add(new PathPatternParserServerWebExchangeMatcher(parse));
        }
        return pathPatterns;
    }
}
